备忘录【Memento】

实例

  1. 发起人类:创建备忘录,恢复备忘录
  2. 备忘录类:设置状态,获取状态
  3. 管理者类:设置备忘录,获取备忘录
    注: 备忘录模式如果要备份对象,则必须用clone, 否则原始备份对象更改会影响备忘录里保存的对象(因为传对象默认为引用传值)
// 发起人类
class Originator
{
    private $state;
    public function setState($state)
    {
        $this->state = $state;
    }
    public function getState()
    {
        return $this->state;
    }
    // 保存并创建备忘录
    public function setMemento()
    {
        return new Memento($this->getState());
    }
    // 恢复备忘录,将memento导入并将相关数据恢复
    public function getMemento(Memento $memento)
    {
        $this->state = $memento->getState();
    }
    // 显示数据
    public function show()
    {
        echo 'status ', $this->state, PHP_EOL;
    }
}
// 备忘录类
class Memento
{
    private $state;
    function __construct($state)
    {
        $this->state = $state;
    }
    public function getState()
    {
        return $this->state;
    }
}
// 管理者类
class CareTaker
{
    private $memento;
    public function getMemento()
    {
        return $this->memento;
    }
    public function setMemento(Memento $memento)
    {
        $this->memento = $memento;
    }
}
$originator = new Originator(); // Originator初始状态,状态属性on
$originator->setState('On');
$originator->show();
// 保存状态时,由于有了很好的封装,可以隐藏Originator的实现细节
$carataker = new CareTaker();
$carataker->setMemento($originator->setMemento());
// 改变属性
$originator->setState('Off');
$originator->show();
// 恢复属性
$originator->getMemento($carataker->getMemento());
$originator->show();

总结

备忘录模式在不暴露对象内部细节的前提下,通过专门的备忘录对象存储和管理历史状态,从而提供可恢复和撤销的能力,实现“后悔药”机制。

意图

不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存,以便将来能够恢复到该历史状态。

主要解决

提供“后悔药”机制,允许用户取消不确定或错误的操作,回到先前状态。

何时使用

很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有 "后悔药" 可吃。

如何解决

引入一个备忘录类专门存储状态,并使用一个管理类来存储和管理这些备忘录对象(符合迪米特原则)。

关键代码

客户不与备忘录类耦合,与备忘录管理类耦合。

优点

缺点

消耗资源。如果状态信息过多,每次保存都会占用大量的内存

使用场景

需要保存/恢复数据的相关状态,或提供可回滚操作的场景(如游戏存档、Ctrl + Z 撤销、数据库事务)。

注意事项

可与命令模式结合实现撤销功能;可与原型模式结合使用,利用克隆来优化备忘录的创建过程,从而节约内存